package com.ywu.standard.gateway.service.handler;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ywu.api.spi.dto.ApiRouterDto;
import com.ywu.api.spi.entity.ApiRouterEntity;
import com.ywu.api.spi.entity.RouterCORS;
import com.ywu.api.spi.spi.ApiRouterSpi;
import com.ywu.app.manage.spi.entity.YwuAppEntity;
import com.ywu.common.constant.CommonConstant;
import com.ywu.common.result.CommonResult;
import com.ywu.standard.gateway.entity.GatewayFilterEntity;
import com.ywu.standard.gateway.redis.RedisGatewayRouter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static org.springframework.cloud.gateway.support.RouteMetadataUtils.RESPONSE_TIMEOUT_ATTR;

/**
 *
 * 核心配置类，项目初始化加载数据库的路由配置
 *
 */
@Service
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner {

    private final static Logger log = LoggerFactory.getLogger(GatewayServiceHandler.class);

    @Autowired
    private RedisGatewayRouter redisGatewayRouter;

    @Autowired
    private ApiRouterSpi apiRouterSpi;

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    /**
     * @Author GroundDemo
     * @Description springboot启动后执行
     * @Date 23:08 2024/2/15
     * @Param [args]
     * @return void
     **/
    @Override
    public void run(String... args){
        this.loadRouteConfig();
    }

    /**
     * @Author GroundDemo
     * @Description 删除redis缓存
     * @Date 22:11 2024/2/15
     * @Param []
     * @return void
     **/
    public void deleteRedisCache() {
        redisGatewayRouter.clearCache();
    }

    /**
     * @Author GroundDemo
     * @Description 全量加载数据库中接口授权
     * @Date 16:50 2024/2/15
     * @Param []
     * @return com.ywu.blog.common.result.CommonResult<java.lang.Object>
     **/
    public CommonResult<Object> loadRouteConfig() {
        CompletableFuture<CommonResult<List<ApiRouterEntity>>> commonResultCompletableFuture =
                CompletableFuture.supplyAsync(() -> apiRouterSpi.queryAllRoutesSpi());
        CommonResult<List<ApiRouterEntity>> spiRes;
        try {
            spiRes = commonResultCompletableFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("loadRouteConfig query api info error {}", e.getMessage());
            return CommonResult.error().setCode("loadRouteConfig query api info error");
        }
        //从数据库拿到路由配置
        if (!spiRes.getCode().equals("200")) {
            log.error("loadRouteConfig error!! api route spi error {}", spiRes.getMessage());
            return CommonResult.error().setErrorCode("loadRouteConfig error").setMessage(spiRes.getMessage());
        }
        List<ApiRouterEntity> gatewayRouteList = spiRes.getData();
        gatewayRouteList.forEach(res -> {
            RouteDefinition definition = new RouteDefinition();
            Map<String, String> predicateParams = new HashMap<>(8);
            PredicateDefinition predicate = new PredicateDefinition();
            URI uri = URI.create(res.getRoutePath());
            definition.setId(res.getCode());
            // 名称是固定的，spring gateway会根据名称找对应的PredicateFactory
            predicate.setName("Path");
            predicateParams.put("pattern",res.getPredicates());
            predicate.setArgs(predicateParams);
            // 设置过滤器相关信息
            setGatewayFilter(definition, res);
            definition.setPredicates(Collections.singletonList(predicate));
            definition.setUri(uri);
            redisGatewayRouter.save(Mono.just(definition)).subscribe();
        });
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 设置网关过滤器
    * @Date 10:38 2025/4/17
    * @Param [definition, entity]
    * @return void
    **/
    private void setGatewayFilter(RouteDefinition definition, ApiRouterEntity entity) {
        List<FilterDefinition> filters = new ArrayList<>();
        // 配置跨域的头
        switch (entity.getCorsState()) {
            case ALLOW_CORS:
                FilterDefinition filter = new FilterDefinition();
                filter.setName("AddResponseHeader");
                HashMap<String, String> stringStringHashMap = new HashMap<>();
                stringStringHashMap.put("_genkey_0", "Access-Control-Allow-Origin");
                stringStringHashMap.put("_genkey_1", "*");
                filter.setArgs(stringStringHashMap);
                filters.add(filter);
                break;
            case REJECT_CORS:
            default:
                break;
        }
        HashMap<String, Object> metaDataMap = new HashMap<>();
        // 配置超时时间
        if (entity.getTimeOut()) {
            metaDataMap.put(RESPONSE_TIMEOUT_ATTR, entity.getTimeOutNum());
        }
        definition.setMetadata(metaDataMap);
        definition.setFilters(filters);
    }

    /**
     * @Author GroundDemo
     * @Description 解析json对象
     * @Date 17:26 2024/2/15
     * @Param [filterJson]
     * @return java.util.List<com.ywu.blog.gateway.entity.GatewayFilterEntity>
     **/
    private List<GatewayFilterEntity> parseJsonToEntity(String filterJson) {
        List<GatewayFilterEntity> filterRes = new ArrayList<>();
        JSONArray objects = JSON.parseArray(filterJson);
        ObjectMapper objectMapper = new ObjectMapper();
        for (int i = 0; i < objects.size(); i++) {
            JSONObject jsonObject = objects.getJSONObject(i);
            try {
                GatewayFilterEntity gatewayFilterEntity = objectMapper.readValue(jsonObject.toJSONString(),
                        GatewayFilterEntity.class);
                filterRes.add(gatewayFilterEntity);
            } catch (JsonProcessingException e) {
                log.error(e.getMessage());
            }
        }
        return filterRes;
    }

    /**
     * @Author GroundDemo
     * @Description 删除路由并删除缓存
     * @Date 22:56 2024/2/15
     * @Param [routeId]
     * @return void
     **/
    public void deleteRoute(String routeId){
        redisGatewayRouter.delete(Mono.just(routeId)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
    * @Author GroundDemo
    * @Description 添加路由
    * @Date 13:57 2025/3/21
    * @Param [gatewayRouterDto]
    * @return com.ywu.blog.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> addApiRoute(ApiRouterDto gatewayRouterDto) {
        RouteDefinition definition = new RouteDefinition();
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();
        predicate.setName("Path");
        definition.setId(gatewayRouterDto.getCode());
        URI uri = URI.create(gatewayRouterDto.getRoutePath());
        definition.setUri(uri);
        // 名称是固定的，spring gateway会根据名称找对应的PredicateFactory
        predicateParams.put("pattern",gatewayRouterDto.getPredicates());
        predicate.setArgs(predicateParams);
        // 设置过滤器相关信息
        ApiRouterEntity entity = new ApiRouterEntity();
        BeanUtils.copyProperties(gatewayRouterDto, entity);
        setGatewayFilter(definition, entity);
        definition.setPredicates(Collections.singletonList(predicate));
        redisGatewayRouter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return CommonResult.success(null);
    }
}